/**
 * 
 */
package gates.internals;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Properties;

/**
 * @author denis
 * 
 */
public class Transistor implements ITransistor {
	protected Base base; // Transistor-gate
	protected Emitter emitter; // Transistor-sink
	protected Collector collector; // Transistor-source
	protected boolean defective;
	protected String name;

	public Transistor() {
	}

	public Transistor(String name, int defect) {
		base = new Base();
		emitter = new Emitter();
		collector = new Collector();
		defective = defect == 1;
		base.addObserver(collector);
		this.name = name;
		// collector.addObserver(emitter);
	}

	public Properties getStateAsProperties(Properties properties) {
		properties.setProperty(name + ".b", String.valueOf(base.isOn()));
		properties.setProperty(name + ".e", String.valueOf(emitter.isOn()));
		properties.setProperty(name + ".c", String.valueOf(collector.isOn()));
		return properties;
	}

	public void setOldValues(Boolean base, Boolean emitter, Boolean collector) {
		this.base.setOldValue(base);
		this.emitter.setOldValue(emitter);
		this.collector.setOldValue(collector);
	}

	public Base getBase() {
		return base;
	}

	public Emitter getEmitter() {
		return emitter;
	}

	public Collector getCollector() {
		return collector;
	}

	public String getName() {
		return name;
	}

	abstract class Gate extends Observable implements Observer {
		Boolean on = null;
		Boolean oldValue;
		private List<Observer> observers = new ArrayList<Observer>();

		Boolean isOn() {
			return on;
		}

		void switchGate(boolean on) {
			this.on = on;
			notifyObservers();
		}

		void setOldValue(Boolean oldValue) {
			this.oldValue = oldValue;
		}

		public abstract void update(Observable o, Object arg);

		public void addObserver(Observer obs) {
			if (observers.contains(obs)) {
				return;
			}
			observers.add(obs);
			// notifyObserver(obs);
		}

		public void notifyObservers() {
			for (Observer obs : observers) {
				notifyObserver(obs);
			}
		}

		public void notifyObserver(Observer obs) {
			obs.update(this, on);
		}
	}

	public class Base extends Gate {
		private List<Gate> inputs = new ArrayList<Gate>();

		public void update(Observable o, Object arg) {
			if (((Boolean) arg) == true) {
				if (o instanceof Gate && !inputs.contains(o))
					inputs.add((Gate) o);
				this.switchGate(true);
			}
			if (((Boolean) arg) == false && !inputs.isEmpty()) {
				if (o instanceof Gate && inputs.contains(o)) {
					inputs.remove(o);
				}
				if (inputs.isEmpty())
					this.switchGate(false);
			}
		}

		public boolean value() {
			return on;
		}
	}

	public class Emitter extends Gate {
		public void update(Observable o, Object arg) {
			switchGate((Boolean) arg);
		}

		public boolean value() {
			return on;
		}
	}

	public class Collector extends Gate {
		public void update(Observable o, Object arg) {
			if (o instanceof Base) {
				if (emitter.isOn() == null) {
					return;
				}
				if (defective) {
					System.out.println("Defective: " + Transistor.this.toString());
					if (oldValue == null) {
						System.out
								.println("Defective transistor with no oldValue, not transmitting anything!"
										+ Transistor.this.toString());
					} else {
						switchGate(oldValue);
					}
				} else {
					switchGate((Boolean) arg && emitter.isOn());
				}
			} else if (o instanceof Emitter && (Emitter) o != emitter) {
				switchGate((Boolean) arg);
			}
		}

		public boolean value() {
			return on;
		}
	}

	@Override
	public String toString() {
		return "transistor " + name;
	}
}
